/*
*  Copyright 2019-2020 Zheng Jie
*
*  Licensed under the Apache License, Version 2.0 (the "License");
*  you may not use this file except in compliance with the License.
*  You may obtain a copy of the License at
*
*  http://www.apache.org/licenses/LICENSE-2.0
*
*  Unless required by applicable law or agreed to in writing, software
*  distributed under the License is distributed on an "AS IS" BASIS,
*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*  See the License for the specific language governing permissions and
*  limitations under the License.
*/
package me.zhengjie.modules.gis.service.impl;

import cn.hutool.core.util.ArrayUtil;
import com.alibaba.fastjson.JSONArray;
import me.zhengjie.config.DistrictEnum;
import me.zhengjie.modules.gis.domain.*;
import me.zhengjie.modules.gis.service.dto.*;
import me.zhengjie.utils.*;
import lombok.RequiredArgsConstructor;
import me.zhengjie.modules.gis.repository.TransportationOutHistoryRepository;
import me.zhengjie.modules.gis.service.TransportationOutHistoryService;
import me.zhengjie.modules.gis.service.mapstruct.TransportationOutHistoryMapper;
import me.zhengjie.utils.map.LonlatConver;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.Timestamp;
import java.util.*;
import java.io.IOException;
import javax.persistence.criteria.Predicate;
import javax.servlet.http.HttpServletResponse;

/**
* @website https://el-admin.vip
* @description 服务实现
* @author ZhuangBing Yan
* @date 2022-09-07
**/
@Service
@RequiredArgsConstructor
public class TransportationOutHistoryServiceImpl implements TransportationOutHistoryService {

    private final TransportationOutHistoryRepository transportationOutHistoryRepository;
    private final TransportationOutHistoryMapper transportationOutHistoryMapper;

    @Override
    public Map<String,Object> queryAll(TransportationOutHistoryQueryCriteria criteria, Pageable pageable){
        Page<TransportationOutHistory> page = transportationOutHistoryRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder),pageable);
        return PageUtil.toPage(page.map(transportationOutHistoryMapper::toDto));
    }

    @Override
    public List<TransportationOutHistoryDto> queryAll(TransportationOutHistoryQueryCriteria criteria){
        return transportationOutHistoryMapper.toDto(transportationOutHistoryRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder)));
    }

    @Override
    @Transactional
    public TransportationOutHistoryDto findById(Long id) {
        TransportationOutHistory transportationOutHistory = transportationOutHistoryRepository.findById(id).orElseGet(TransportationOutHistory::new);
        ValidationUtil.isNull(transportationOutHistory.getId(),"TransportationOutHistory","id",id);
        return transportationOutHistoryMapper.toDto(transportationOutHistory);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public TransportationOutHistoryDto create(TransportationOutHistory resources) {
        return transportationOutHistoryMapper.toDto(transportationOutHistoryRepository.save(resources));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(TransportationOutHistory resources) {
        TransportationOutHistory transportationOutHistory = transportationOutHistoryRepository.findById(resources.getId()).orElseGet(TransportationOutHistory::new);
        ValidationUtil.isNull( transportationOutHistory.getId(),"TransportationOutHistory","id",resources.getId());
        transportationOutHistory.copy(resources);
        transportationOutHistoryRepository.save(transportationOutHistory);
    }

    @Override
    public void deleteAll(Long[] ids) {
        for (Long id : ids) {
            transportationOutHistoryRepository.deleteById(id);
        }
    }

    @Override
    public void download(List<TransportationOutHistoryDto> all, HttpServletResponse response) throws IOException {
        List<Map<String, Object>> list = new ArrayList<>();
        for (TransportationOutHistoryDto transportationOutHistory : all) {
            Map<String,Object> map = new LinkedHashMap<>();
            map.put("车牌号", transportationOutHistory.getCarNumber());
            map.put("卡号", transportationOutHistory.getCardNumber());
            map.put("货物名称", transportationOutHistory.getGoodName());
            map.put("发货单位", transportationOutHistory.getDeliverCompany());
            map.put("区域", transportationOutHistory.getArea());
            map.put("运输单位", transportationOutHistory.getFeedCompany());
            map.put("处置单位", transportationOutHistory.getDisposeCompany());
            map.put("进厂时间", transportationOutHistory.getEnterInTime());
            map.put("毛重（吨）", transportationOutHistory.getGrossWeight());
            map.put("皮重(吨)", transportationOutHistory.getTareWeight());
            map.put("扣减(吨)", transportationOutHistory.getDeductionWeight());
            map.put("净重(吨)", transportationOutHistory.getNetWeight());
            map.put("出单人(创建人)", transportationOutHistory.getCreateBy());
            map.put("车间名称", transportationOutHistory.getWorkShopName());
            map.put("数据类型()", transportationOutHistory.getType());
            map.put("数据状态", transportationOutHistory.getStatus());
            map.put("是否删除( 0 未删除  1 已删除)", transportationOutHistory.getIsDel());
            map.put("创建时间", transportationOutHistory.getCreateTime());
            map.put("修改人", transportationOutHistory.getUpdateBy());
            map.put("修改时间", transportationOutHistory.getUpdateTime());
            map.put("起点纬度坐标", transportationOutHistory.getPositionStart());
            map.put("路经线坐标", transportationOutHistory.getLinePosition());
            map.put("终点经度坐标", transportationOutHistory.getPositionEnd());
            list.add(map);
        }
        FileUtil.downloadExcel(list, response);
    }

    //获取循环应用统计数据
    @Override
    public RecyclingDto getGisRecycling(Integer year) {
        if (year == null) {
            year = Calendar.getInstance().get(Calendar.YEAR);
        }
        String start = year + "-01-01 00:00:00";
        String end = (year+1) + "-01-01 00:00:00";
        List<TransportationOutHistory> outHistoryList = transportationOutHistoryRepository.findAll((root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> res = new ArrayList<>();
            res.add(criteriaBuilder.between(root.get("enterInTime"), new Timestamp(DateUtil.parseDate(start, DateUtil.FORMAT_YYYY_MM_DD_HH_MM_SS).getTime()),
                    new Timestamp(DateUtil.parseDate(end, DateUtil.FORMAT_YYYY_MM_DD_HH_MM_SS).getTime())));
            return criteriaQuery.where(res.toArray(new Predicate[0])).getRestriction();
        });
        //创建结果对象
        RecyclingDto result = new RecyclingDto();
        result.setYear(year);
        if (outHistoryList.isEmpty()) {
            return result;
        }
        //月度统计
        HashMap<Integer, BigDecimal> monthlyEmissionMap = new HashMap<>();
        for (int i = 1; i <= 12; i++) {
            monthlyEmissionMap.put(i, BigDecimal.ZERO);
        }
        //辖区排放量统计
        HashMap<String, BigDecimal> districtEmissionMap = new HashMap<>();
        for (DistrictEnum districtEnum: DistrictEnum.values()) {
            if (!"苏州市".equals(districtEnum.getCity())) {
                continue;
            }
            districtEmissionMap.put(districtEnum.getDistrict(), BigDecimal.ZERO);
        }
        //具体地点的排放量统计
        HashMap<String, LineDto> locationEmissionMap = new HashMap<>();

        //遍历数据
        for (TransportationOutHistory outHistory: outHistoryList) {
            BigDecimal emit = outHistory.getNetWeight();
            if (emit == null || emit.equals(BigDecimal.ZERO) || outHistory.getEnterInTime() == null) {
                continue;
            }
            Timestamp timestamp = outHistory.getEnterInTime();
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(timestamp);
            int month = calendar.get(Calendar.MONTH);
            BigDecimal mon = monthlyEmissionMap.get(month);
            if (mon != null) {
                monthlyEmissionMap.put(month, mon.add(emit));
            }
            String district = outHistory.getArea();
            if (StringUtils.isNotBlank(district) && districtEmissionMap.containsKey(district)) {
                districtEmissionMap.put(district, districtEmissionMap.get(district).add(emit));
            }
            String disposeCompany = outHistory.getDisposeCompany();
            if (StringUtils.isNotBlank(disposeCompany) && StringUtils.isNotBlank(outHistory.getLinePosition())) {
                LineDto data = locationEmissionMap.get(disposeCompany);
                if (data == null) {
                    data = new LineDto();
                    if (StringUtils.isNotBlank(outHistory.getPositionEnd())) {
                        String[] splits = outHistory.getPositionEnd().split(",");
                        double[] centers = LonlatConver.gcj02_To_Wgs84(Double.parseDouble(splits[0]), Double.parseDouble(splits[1]));
                        data.setPosition(centers[0] + "," + centers[1]);
                    }
                    List<BigDecimal[]> linePoints = JSONArray.parseArray(outHistory.getLinePosition(), BigDecimal[].class);
                    linePoints.forEach(ArrayUtil::reverse);
                    data.setLine(linePoints);
                    data.setSumWeight(BigDecimal.ZERO);
                    data.setDistrict(district);
                    data.setType(true);//true/1为运出， 0/false运进
                }
                List<LineContentDto> contents = data.getContent();
                if (contents == null) {
                    contents = new ArrayList<>();
                }
                LineContentDto lineContentDto = new LineContentDto();
                lineContentDto.setEnterTime(timestamp);
                lineContentDto.setWeight(emit);
                lineContentDto.setStatus(false);
                contents.add(lineContentDto);
                data.setContent(contents);

                data.setSumWeight(data.getSumWeight().add(emit));
                locationEmissionMap.put(disposeCompany, data);
            }
        }

        //按月度排放统计
        List<RecyclingData> monthlyEmissionList = new ArrayList<>();
        BigDecimal totalRecycling = BigDecimal.ZERO;
        for (Map.Entry<Integer, BigDecimal> entry: monthlyEmissionMap.entrySet()) {
            RecyclingData data = new RecyclingData();
            data.setType(1);
            data.setMonth(String.valueOf(entry.getKey()));
            data.setRecycling(entry.getValue());
            totalRecycling = totalRecycling.add(entry.getValue());
            monthlyEmissionList.add(data);
        }
        result.setMonthlyRecycling(monthlyEmissionList);
        result.setTotalRecycling(totalRecycling);

        //按地区排放统计
        List<RecyclingData> districtEmissionList = new ArrayList<>();
        for (Map.Entry<String, BigDecimal> entry: districtEmissionMap.entrySet()) {
            DistrictEnum districtEnum = DistrictEnum.getByDistrictName("苏州市", entry.getKey());
            double[] gpsLoc = LonlatConver.gcj02_To_Wgs84(districtEnum.getLng().doubleValue(), districtEnum.getLat().doubleValue());
            String centerPosition = gpsLoc[0] + "," + gpsLoc[1];
            //地区排放总量
            RecyclingData districtEmission = new RecyclingData();
            districtEmission.setType(2);
            districtEmission.setDistrict(entry.getKey());
            districtEmission.setRecycling(entry.getValue());
            districtEmission.setCenterPosition(centerPosition);
            districtEmissionList.add(districtEmission);
        }
        result.setDistrictRecycling(districtEmissionList);

        //按地点排放统计
        result.setLocationRecycling(new ArrayList<>(locationEmissionMap.values()));

        return result;
    }

    //获取减量排放统计数据
    @Override
    public ReduceEmissionDto getGisReduceEmission(Integer year) {
        if (year == null) {
            year = Calendar.getInstance().get(Calendar.YEAR);
        }
        String start = year + "-01-01 00:00:00";
        String end = (year+1) + "-01-01 00:00:00";
        List<TransportationOutHistory> outHistoryList = transportationOutHistoryRepository.findAll((root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> res = new ArrayList<>();
            res.add(criteriaBuilder.between(root.get("enterInTime"), new Timestamp(DateUtil.parseDate(start, DateUtil.FORMAT_YYYY_MM_DD_HH_MM_SS).getTime()),
                    new Timestamp(DateUtil.parseDate(end, DateUtil.FORMAT_YYYY_MM_DD_HH_MM_SS).getTime())));
            return criteriaQuery.where(res.toArray(new Predicate[0])).getRestriction();
        });
        //创建结果对象
        ReduceEmissionDto result = new ReduceEmissionDto();
        result.setYear(year);
        if (outHistoryList.isEmpty()) {
            return result;
        }
        //月度统计
        HashMap<Integer, BigDecimal> monthlyEmissionMap = new HashMap<>();
        for (int i = 1; i <= 12; i++) {
            monthlyEmissionMap.put(i, BigDecimal.ZERO);
        }
        //辖区排放量统计
        HashMap<String, BigDecimal> districtEmissionMap = new HashMap<>();
        //辖区排放数量统计
        HashMap<String, Integer> districtCountMap = new HashMap<>();
        for (DistrictEnum districtEnum: DistrictEnum.values()) {
            if (!"苏州市".equals(districtEnum.getCity())) {
                continue;
            }
            districtEmissionMap.put(districtEnum.getDistrict(), BigDecimal.ZERO);
            districtCountMap.put(districtEnum.getDistrict(), 0);
        }
        //具体地点的排放量统计
        HashMap<String, ReduceEmissionData> locationEmissionMap = new HashMap<>();

        //遍历数据
        for (TransportationOutHistory outHistory: outHistoryList) {
            BigDecimal emit = outHistory.getNetWeight();
            if (emit == null || emit.equals(BigDecimal.ZERO) || outHistory.getEnterInTime() == null) {
                continue;
            }
            Timestamp timestamp = outHistory.getEnterInTime();
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(timestamp);
            int month = calendar.get(Calendar.MONTH);
            BigDecimal mon = monthlyEmissionMap.get(month);
            if (mon != null) {
                monthlyEmissionMap.put(month, mon.add(emit));
            }
            String district = outHistory.getArea();
            if (StringUtils.isNotBlank(district) && districtEmissionMap.containsKey(district)) {
                districtEmissionMap.put(district, districtEmissionMap.get(district).add(emit));
                districtCountMap.put(district, districtCountMap.get(district) + 1);
            }
            String disposeCompany = outHistory.getDisposeCompany();
            if (StringUtils.isNotBlank(disposeCompany)) {
                ReduceEmissionData data = locationEmissionMap.get(disposeCompany);
                if (data == null) {
                    data = new ReduceEmissionData();
                    data.setDistrict(district);
                    if (StringUtils.isNotBlank(outHistory.getPositionEnd())) {
                        String[] splits = outHistory.getPositionEnd().split(",");
                        double[] centers = LonlatConver.gcj02_To_Wgs84(Double.parseDouble(splits[0]), Double.parseDouble(splits[1]));
                        data.setCenterPosition(centers[0] + "," + centers[1]);
                    }
                    data.setEmission(BigDecimal.ZERO);
                    data.setType(5);
                }
                data.setEmission(data.getEmission().add(emit));
                locationEmissionMap.put(disposeCompany, data);
            }
        }

        //按月度排放统计
        List<ReduceEmissionData> monthlyEmissionList = new ArrayList<>();
        BigDecimal totalEmission = BigDecimal.ZERO;
        for (Map.Entry<Integer, BigDecimal> entry: monthlyEmissionMap.entrySet()) {
            ReduceEmissionData data = new ReduceEmissionData();
            data.setType(1);
            data.setMonth(String.valueOf(entry.getKey()));
            data.setEmission(entry.getValue());
            totalEmission = totalEmission.add(entry.getValue());
            monthlyEmissionList.add(data);
        }
        result.setMonthlyEmission(monthlyEmissionList);
        result.setTotalEmission(totalEmission);
        result.setDailyEmission(totalEmission.divide(DateUtil.isLeafYear(year) ? new BigDecimal("366") : new BigDecimal("365"),2, RoundingMode.CEILING));

        //按地区排放统计
        List<ReduceEmissionData> districtEmissionList = new ArrayList<>();
        List<ReduceEmissionData> districtCountList = new ArrayList<>();
        List<ReduceEmissionData> districtAverageList = new ArrayList<>();
        int totalCount = 0;
        for (Map.Entry<String, BigDecimal> entry: districtEmissionMap.entrySet()) {
            DistrictEnum districtEnum = DistrictEnum.getByDistrictName("苏州市", entry.getKey());
            double[] gpsLoc = LonlatConver.gcj02_To_Wgs84(districtEnum.getLng().doubleValue(), districtEnum.getLat().doubleValue());
            String centerPosition = gpsLoc[0] + "," + gpsLoc[1];
            //地区排放总量
            ReduceEmissionData districtEmission = new ReduceEmissionData();
            districtEmission.setType(2);
            districtEmission.setDistrict(entry.getKey());
            districtEmission.setEmission(entry.getValue());
            districtEmission.setCenterPosition(centerPosition);
            districtEmissionList.add(districtEmission);
            //地区排放项目数量
            ReduceEmissionData districtCount = new ReduceEmissionData();
            districtCount.setType(3);
            districtCount.setDistrict(entry.getKey());
            districtCount.setProjectCount(districtCountMap.get(entry.getKey()));
            districtCount.setCenterPosition(centerPosition);
            districtCountList.add(districtCount);
            totalCount = totalCount + districtCountMap.get(entry.getKey());
            //地区人均排放量
            ReduceEmissionData districtAverage = new ReduceEmissionData();
            districtAverage.setType(4);
            districtAverage.setDistrict(entry.getKey());
            districtAverage.setEmission(entry.getValue().divide(districtEnum.getPopulation().multiply(new BigDecimal("10000")), 2, RoundingMode.CEILING));
            districtAverage.setCenterPosition(centerPosition);
            districtAverageList.add(districtAverage);
        }
        result.setDistrictEmission(districtEmissionList);
        result.setDistrictAverageEmission(districtAverageList);
        result.setDistrictProjectCount(districtCountList);
        result.setTotalTearDownCount(totalCount);

        //按地点排放统计
        result.setLocationEmission(new ArrayList<>(locationEmissionMap.values()));

        return result;
    }
}